﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.NonlinearEquationSolvers;

namespace SmartMathLibrary.Optimization
{
    /// <summary>
    /// This class provides the finding of real extremums by using the Bairstow algorithm.
    /// </summary>
    [Serializable]
    public class PolynomialOptimizer
    {
        /// <summary>
        /// The polynomial for which the extremum should be found.
        /// </summary>
        private SimplePolynomial polynomial;

        /// <summary>
        /// Initializes a new instance of the <see cref="PolynomialOptimizer"/> class.
        /// </summary>
        /// <param name="polynomial">The polynomial for which the extremum should be found.</param>
        public PolynomialOptimizer(SimplePolynomial polynomial)
        {
            if (polynomial == (SimplePolynomial) null)
            {
                throw new ArgumentNullException("polynomial");
            }

            this.polynomial = polynomial;
        }

        /// <summary>
        /// Gets or sets the polynomial for which the extremum should be found.
        /// </summary>
        /// <value>The polynomial for which the extremum should be found.</value>
        public SimplePolynomial Polynomial
        {
            get { return polynomial; }
            set { polynomial = value; }
        }

        /// <summary>
        /// Finds the real extremums of the specified polynomial.
        /// </summary>
        /// <returns>The real extremums of the specified polynomial.</returns>
        public RealExtremum[] FindExtremum()
        {
            ComplexNumber[] tempuri;
            List<RealExtremum> result = new List<RealExtremum>();
            SimplePolynomial workingPolynomial = this.polynomial.Copy().Derivative();

            if (workingPolynomial.Degree > 2)
            {
                BairstowComplexRootFinder rootFinder = new BairstowComplexRootFinder(workingPolynomial);

                tempuri = rootFinder.FindRoots();
            }
            else if (workingPolynomial.Degree == 2)
            {
                workingPolynomial.Normalize();
                tempuri = ExtendedMath.SolveQuadraticEquationComplex
                    (workingPolynomial.Coefficients[1], workingPolynomial.Coefficients[0]);
            }
            else if (workingPolynomial.Degree == 1)
            {
                LinearFunction function = new LinearFunction(workingPolynomial.Coefficients[1],
                                                             workingPolynomial.Coefficients[0]);
                tempuri = new ComplexNumber[1];

                tempuri[0] = new ComplexNumber(function.FindRoot());
            }
            else
            {
                return new RealExtremum[0];
            }

            SimplePolynomial checkingPolynomial = this.polynomial.Derivative().Derivative();

            foreach (ComplexNumber complex in tempuri)
            {
                if (complex.ImaginaryNumberPart == 0)
                {
                    double derivativeResult = checkingPolynomial.SolveAt(complex.RealNumberPart);
                    RealExtremum extremum = new RealExtremum(complex.RealNumberPart,
                                                             this.polynomial.SolveAt(complex.RealNumberPart));

                    if (derivativeResult < 0)
                    {
                        extremum.Type = ExtremumType.Maxima;
                    }
                    else if (derivativeResult > 0)
                    {
                        extremum.Type = ExtremumType.Minima;
                    }

                    result.Add(extremum);
                }
            }

            return result.ToArray();
        }
    }
}